Now that you have seen how to persist a single object to a stream, you’re ready to examine how to save a set of objects. As you might have noticed, the Serialize() method of the IFormatter interface does not provide a way to specify an arbitrary number of objects as input (only a single System.Object). On a related note, the return value of Deserialize() is, again, a single System.Object (the same basic limitation holds true for XmlSerializer):
public interface IFormatter { ... object Deserialize(Stream serializationStream); void Serialize(Stream serializationStream, object graph); }
Recall that the System.Object represents a complete tree of objects. Given this, if you pass in an object that has been marked as [Serializable] and contains other [Serializable] objects, the entire set of objects is persisted in a single method call. As luck would have it, most of the types you find in the System.Collections and System.Collections.Generic namespaces have already been marked as [Serializable]. Therefore, if you wish to persist a set of objects, simply add the desired set to the container (such as an ArrayList or a List<T>) and serialize the object to your stream of choice.
Now assume that you want to update the JamesBondCar class with a two-argument constructor, so you can set a few pieces of state data (note that you add back the default constructor as required by the XmlSerializer):
[Serializable, XmlRoot(Namespace = "http://www.MyCompany.com")] public class JamesBondCar : Car { public JamesBondCar(bool skyWorthy, bool seaWorthy) { canFly = skyWorthy; canSubmerge = seaWorthy; } // The XmlSerializer demands a default constructor! public JamesBondCar(){} ... }
With this, you can now persist any number of JamesBondCars:
static void SaveListOfCars() { // Now persist a List<T> of JamesBondCars. List<JamesBondCar> myCars = new List<JamesBondCar>(); myCars.Add(new JamesBondCar(true, true)); myCars.Add(new JamesBondCar(true, false)); myCars.Add(new JamesBondCar(false, true)); myCars.Add(new JamesBondCar(false, false)); using(Stream fStream = new FileStream("CarCollection.xml", FileMode.Create, FileAccess.Write, FileShare.None)) { XmlSerializer xmlFormat = new XmlSerializer(typeof(List<JamesBondCar>)); xmlFormat.Serialize(fStream, myCars); } Console.WriteLine("=> Saved list of cars!"); }
You use XmlSerializer here, so you are required to specify type information for each of the subobjects within the root object (List<JamesBondCar>, in this case). However, the logic would be even more straightforward if you were to use the BinaryFormatter or SoapFormatter type, instead:
static void SaveListOfCarsAsBinary() { // Save ArrayList object (myCars) as binary. List<JamesBondCar> myCars = new List<JamesBondCar>(); BinaryFormatter binFormat = new BinaryFormatter(); using(Stream fStream = new FileStream("AllMyCars.dat", FileMode.Create, FileAccess.Write, FileShare.None)) { binFormat.Serialize(fStream, myCars); } Console.WriteLine("=> Saved list of cars in binary!"); }